Teoria portfelowa

…po angielsku modern portfolio theory lub mean–variance analysis.

Główna idea – Efektywna granica

…czyli Efficient frontier.

To zbiór porfeli, dla których nie istnieje inny portfel o wyższym oczekiwanym zwrocie przy tym samym odchyleniu standardowym zwrotu (ryzyku).

Szukamy takich wag, które pozwolą nam zminimalizować wariancję

\[ \min_{\mathbf{w}} = \mathbf{w}^T\Sigma\mathbf{w}. \]

Dla dobranych wag wartość oczekiwana zwrotu powinna odpowiadać ustalonemu poziomowi

\[ \mathbf{w}^T\mathbf{\mu} = \mu_p. \]

Wagi muszą sumować się do 1

\[ \mathbf{w}^T\mathbf{1}=1. \]

Graficzna reprezentacja

Źródło: https://inyova.ch/en/expertise/efficient-frontier-investment-theory/

Biblioteka PyortfolioOpt

Jak wyglądają nasze dane?

                AAPL        GE     AMD  ...       BBY       PFE       JPM
date                                    ...                              
1989-12-29  0.117203  0.352438  3.9375  ...  0.166287  0.110818  1.827968
1990-01-02  0.123853  0.364733  4.1250  ...  0.173216  0.113209  1.835617
1990-01-03  0.124684  0.364050  4.0000  ...  0.194001  0.113608  1.896803

[3 rows x 10 columns]

Importujemy potrzebne funkcje

from pypfopt import expected_returns
from pypfopt import risk_models

Liczymy średnie zwroty dla danych…

mu = expected_returns.mean_historical_return(stock_prices)

Pierwsze 5 wyników dla średnich zwrotów

AAPL    0.294305
GE      0.136009
AMD     0.032850
WMT     0.120012
BAC     0.105540
dtype: float64

Liczymy macierz kowariancji zwrotów dla danych…

Sigma = risk_models.sample_cov(stock_prices)

Pierwsze 5 wyników dla macierzy kowariancji

          AAPL        GE       AMD       WMT       BAC
AAPL  0.207537  0.038348  0.084594  0.026599  0.046262
GE    0.038348  0.083109  0.048893  0.029263  0.063275
AMD   0.084594  0.048893  0.390586  0.027288  0.068712
WMT   0.026599  0.029263  0.027288  0.069092  0.030757
BAC   0.046262  0.063275  0.068712  0.030757  0.180192

Wybór portfela

from pypfopt.efficient_frontier import EfficientFrontier

Liczymy Efficient frontier…

ef_return = EfficientFrontier(mu, Sigma)
ef_sharpe = EfficientFrontier(mu, Sigma)
ef_vol = EfficientFrontier(mu, Sigma)


Wybieramy porfel o rocznej stopie zwrotu 20%…

ef_return.efficient_return(0.2)

Wagi

ef_return.clean_weights()
AAPL: 0.16759
GE: 0.00000
AMD: 0.00000
WMT: 0.08029
BAC: 0.00000
T: 0.05185
XOM: 0.28483
BBY: 0.07526
PFE: 0.34018
JPM: 0.00000

Statystyki

ef_return.portfolio_performance(verbose=True)
Expected annual return: 20.0%
Annual volatility: 19.9%
Sharpe Ratio: 1.01
(np.float64(0.19999999999999996), np.float64(0.19898336191007596), np.float64(1.0051091612894922))


Wybieramy porfel o najwyższym wskaźniku Sharpe’a…

ef_sharpe.max_sharpe()

Wagi

AAPL: 0.20430
GE: 0.00000
AMD: 0.00000
WMT: 0.03511
BAC: 0.00000
T: 0.00000
XOM: 0.26966
BBY: 0.09112
PFE: 0.39980
JPM: 0.00000

Statystyki

Expected annual return: 21.5%
Annual volatility: 21.3%
Sharpe Ratio: 1.01
(np.float64(0.21532248096661108), np.float64(0.2127665321341155), np.float64(1.0120129270654488))


Wybieramy porfel o najmniejszej zmienności (odchyleniu standardowym zwrotów)…

ef_vol.min_volatility()

Wagi

AAPL: 0.04306
GE: 0.05511
AMD: 0.00000
WMT: 0.21254
BAC: 0.00000
T: 0.21768
XOM: 0.31582
BBY: 0.01963
PFE: 0.13616
JPM: 0.00000

Statystyki

Expected annual return: 14.8%
Annual volatility: 17.7%
Sharpe Ratio: 0.84
(np.float64(0.14823493562996315), np.float64(0.17710888575470815), np.float64(0.8369706296682664))

Alternatywne sposoby obliczania średniej i wariancji

Zwroty i ryzyko ważone wykładniczo

Nadajemy większe wagi nowym danym.

mu_exp = expected_returns.ema_historical_return(stock_prices, span=180, 
  frequency=252)
Sigma_exp = risk_models.exp_cov(stock_prices, span=180, frequency=252)
ef_exp = EfficientFrontier(mu_exp, Sigma_exp)
ef_exp.max_sharpe()

Statystyki

Expected annual return: 35.8%
Annual volatility: 23.3%
Sharpe Ratio: 1.54
(np.float64(0.35775845787745103), np.float64(0.2330451766369705), np.float64(1.5351463739357045))


Semikowariancja

… czyli wariancja ze zwrotów poniżej ustalonego benchamrku.

semi_Sigma = risk_models.semicovariance(stock_prices, 
  benchmark=0, frequency=252)

Pierwsze 5 wyników dla macierzy semikowariancji

          AAPL        GE       AMD       WMT       BAC
AAPL  0.092904  0.025240  0.059902  0.021780  0.033612
GE    0.025240  0.036607  0.037300  0.017666  0.034130
AMD   0.059902  0.037300  0.178330  0.027033  0.052319
WMT   0.021780  0.017666  0.027033  0.031344  0.021281
BAC   0.033612  0.034130  0.052319  0.021281  0.080705

Źródła